package com.example.spring_aop.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @className: TimeAspect
 * @author: 芃
 * @description:
 * @date: 2024/12/30 12:29
 */
@Aspect //定义切面 ->表示这是一个切面类
@Slf4j
@Component
@Order(2)
public class AspectDemo {

    /*
     * 1、切点（Pointcut）-> 定义这个切面的作用范围，需要对哪些方法进行功能增强,通过表达式来描述需要对哪些方法进行增强
     * 2、链接点（JoinPoint）-> 被增强的方法的调用点，可以获取方法参数，返回值等信息，原始方法的详细信息
     * 3、通知（Advice）-> 增强的逻辑，可以是前置通知、后置通知、环绕通知、异常通知等，定义的额外功能部分代码
     * 4、切面（Aspect）-> 切点和通知的集合，可以理解为切面就是把切点和通知关联起来的一个模块，整体的集合=切点+通知
     */


    /*
     * Around：环绕通知，在目标方法调用前后执行额外的功能，可以获取到原始方法的返回值，可以修改返回值，也可以抛出异常
     * Before：前置通知，在目标方法调用前执行额外的功能，可以获取到原始方法的参数，但是不能修改参数，不能获取返回值，不能抛出异常
     * After：后置通知，在目标方法调用后执行额外的功能，可以获取到原始方法的返回值，但是不能修改返回值，不能获取参数，不能抛出异常
     * AfterReturning：后置通知，在目标方法调用后执行额外的功能，可以获取到原始方法的返回值，但是不能修改返回值，不能获取参数，不能抛出异常
     * AfterThrowing：异常通知，在目标方法抛出异常后执行额外的功能，可以获取到原始方法的异常对象，但是不能修改异常对象，不能获取参数，不能获取返回值
     */


    /*
     * 执行顺序：
     * 1、Around方法的前置处理
     * 2、Before方法的前置处理
     * 3、原始方法的调用
     * 5、AfterReturning方法的后置处理
     * 4、After方法的后置处理
     * 6、Around方法的后置处理
     *
     * 发生异常时：
     * 1、Around方法的前置处理
     * 2、Before方法的前置处理
     * 3、原始方法的调用
     * 4、AfterThrowing方法的后置处理
     * 5、After方法的后置处理
     * 注意：当发生异常时，不执行AfterReturning方法和Around方法的后置处理
     */

    //如何切点复用
    @Pointcut("execution(* com.example.spring_aop.service.*.*(..))") //-》如果想要在其他类当中使用该切点，需要把切点声明为public且使用时，类的全限定名称+切点名称
    public void pt(){

    }

    /*
     * 当有多个切面时，Spring会按照AspectJ的顺序来执行，即先执行最先定义的切面，后执行后定义的切面。
     * 按照名称来排序执行，前置的从小到大，后置的从大到小
     * 可以通过Order注解来定义切面执行的优先级~Order的参数越大，优先级越低
     * 优先级越高，before越先执行，after越后执行
     *
     */

    @Around("pt()") //定义切点 -》 定义这个切面的作用域，需要对哪些方法进行功能增强  Around表示环绕通知
    public Object timeCost(ProceedingJoinPoint joinPoint) throws Throwable {//参数joinPoint表示原始方法，调用proceed()方法执行原始方法
        long start = System.currentTimeMillis();//方法执行前的逻辑
        Object result = joinPoint.proceed();
        long end = System.currentTimeMillis();//方法执行后的逻辑
        log.info(joinPoint.toString() + "time cost: " + (end - start) + "ms");
        return result;
    }


    //日志打印参数信息
    @Before("execution(* com.example.spring_aop.service.*.*(..))")//Before表示前置通知
    public void before() throws Throwable {
/*        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            log.info(arg.toString());
        }*/
        log.info("执行Aspect before");
    }


    //日志打印返回值信息
    @After("execution(* com.example.spring_aop.service.*.*(..))")
    public void after() throws Throwable {
/*        Object result = joinPoint.proceed();
        log.info(result.toString());*/
        log.info("执行Aspect after");
    }

    @AfterThrowing("execution(* com.example.spring_aop.service.*.*(..))")
    public void afterThrowing() throws Throwable {
/*        Throwable ex = (Throwable) joinPoint.getSignature();
        log.error(ex.getMessage());*/
        log.info("执行Aspect afterThrowing");
    }

    @AfterReturning("execution(* com.example.spring_aop.service.*.*(..))")
    public void afterReturning() throws Throwable {
        log.info("执行Aspect afterReturning");
    }
}
